Re: CGI security: Escape newlines.

Robert S. Muhlestein (robertm@teleport.com)
Tue, 6 Feb 1996 11:49:45 -0800

On Tue, 6 Feb 1996, Lincoln Stein wrote:

> In general I think that it's much better to search for and accept only
> the good patterns rather than trusting things to work when you exclude
> the bad characters.  People forget that the shell isn't the only
> vulnerable program, and other programs may be subvertible by input
> quite distinct from the set of shell metacharacters.
>
> Lincoln

Ditto.  I use a simply "check" routine to check every acceptable form
variable.  Here are the subs I regularly use to accomplish this.  If you
spot a bug, I'd appreciate a note.  If you use my routines, please
leave a ref to me somewhere.

This example uses Lincoln's CGI.pm to grab form variables, then a few of
my own routines to check info, etc.  Although I use these routines all
the time, I haven't tested the actual example script itself, beware:


__BEGIN__
#!/usr/local/bin/perl

########
##  These routines are normally part of a package that I import:
########

$SENDMAIL    = '/usr/lib/sendmail -t -n';
$DATE        = localtime(time);

##  To keep the perl jobs from taking priority over regular httpd daemons
setpriority(0,0,4);

#------------------------------------------------------------------------
sub log_it {

  my $time    = localtime($^T);
  my $message = $_[0]; $message =~ tr/\n\"/ /;
  ($LOG = $_[1]) unless (defined $LOG);
  my $addr    = $ENV{'REMOTE_ADDR'} || 'LOCAL';
  my $host    = $ENV{'REMOTE_HOST'} || 'LOCAL';

  open(LOG, ">>$LOG") || die("Failed to open log file: $LOG\n");
  printf LOG ("[%s]  %s  %s  \"%s\"\n",$time,$host,$addr,$message);

}

#------------------------------------------------------------------------
sub check {
  my($pname,$OKstring,$OKpexp,$error) = @_;
  $OK{$pname} = $OKstring;
  printf ("%-15s\n%-15s %-20s\n","${pname}=${$pname}",$OKpexp,$OKstring)
    if (defined $DEBUG);
  (${$pname} =~ /^$OKpexp$/) ||
    &error("\"${pname}\" (${$pname}) $ERR  $OKstring\n");
  return 1;
}

#------------------------------------------------------------------------
## Beware of passing unchecked headers from the web!!!
sub send_mail {
  my($to,$from,$subject,$message,@headers) = @_;
  open (MAIL, "|-") || exec($SENDMAIL);
  print MAIL <<EOM;
Date: $DATE
To: $to
From: $from
Subject: $subject
@headers

$message

EOM
  close MAIL;
}

#------------------------------------------------------------------------
sub get_info {

  ## Take over form params
  for ($query->param){
     (s/M_//) ? (@{$_} = $query->param("${&}$_"))
       : (${$_} = $query->param($_));
  }
}

#------------------------------------------------------------------------
sub error {
  my $message = @_[0];
  print  <<EOM;
Content-type: text/htm

Processing Error:

Processing Error:


$message

Please make the necessary corrections.
EOM
  (defined $LOG) && &log_it($message);
  die($message);
}

#------------------------------------------------------------------------
sub redirect {
  my $url = @_[0];
  print <<EOM;
Status: 302 Moved Temporarily
Method: GET
URI: <$url>
Location: $url
Content-type: text/htm

EOM
}
#------------------------------------------------------------------------
sub validpw {
  my $salt = (getpwnam($_[0]))[1];
  return (crypt($_[1], $salt) eq $salt) ? 1: 0 ;
}

#################################################################
###  End Subroutines
#################################################################

###  MAIN (Here's where the untested stuff begins)

use CGI;
$query = new CGI;
get_info;

### Just to check one paramter that might come a little close to a shell
check('PARAM','\w{2,20}','2-20 alphanumerics allowed.');

##  To check for form parameters that aren't going to be passed to a shell,
##  but are at least required:
$REQ = '.{1,1000}';
$REQtxt = 'This field is required (up to 1000 characters).';
for ('PARAM2','PARAM2','PARAM3','PARAM4','PARAM5','PARAM6') {
  check($_,$REQ,$REQtxt);
}

print $query->header;
print $query->start_htm;
$PARAM2
$PARAM3
##  You get the idea ...
EOM
print $query->end_htm;

__END__

Rob Muhlestein
CGI Guy
Teleport Internet Services
http://www.teleport.com/